package com.yixing.tech.system.service.impl;

import com.yixing.tech.common.base.*;
import com.yixing.tech.common.enums.ErrCode;
import com.yixing.tech.common.enums.YesOrNo;
import com.yixing.tech.common.utils.CommonUtil;
import com.yixing.tech.common.utils.DataUtil;
import com.yixing.tech.common.vo.s3.*;
import com.yixing.tech.common.vo.s5.EntitySchema;
import com.yixing.tech.entity.entity.ObjButton;
import com.yixing.tech.entity.entity.ObjConfigType;
import com.yixing.tech.entity.entity.ObjStatus;
import com.yixing.tech.entity.po.Obj;
import com.yixing.tech.system.service.EntityManagerService;
import com.yixing.tech.system.service.IWebPageService;
import lombok.extern.slf4j.Slf4j;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.*;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

/**
 * @author yixing tech
 * at 2022/2/28
 * describe：
 */
@Service
@Slf4j
public class EntityManagerServiceImpl implements EntityManagerService {
    @Autowired
    private BaseMongoDao baseMongoDao;
    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private IWebPageService webPageService;

    @Override
    public void saveOrUpdateConfig(String objId, String type, List<EntitySchema.AttrConfig> configs) throws BusinessException {
        EntitySchema cond = new EntitySchema();
        cond.setId(objId);
        cond.setStatus(ObjStatus.deft.name());
        Obj obj = baseMongoDao.queryOne(Obj.class, cond);
        //已发布不能添加属性
        if (obj == null) {
            throw new BusinessException(ErrCode.OBJ_NOT_EXIST);
        }

        Set<EntitySchema.AttrConfig> set = new HashSet<>();
        set.addAll(configs);
        configs = new ArrayList<>(set);
        EntitySchema objVO = DataUtil.o2o(obj, EntitySchema.class);

        switch (ObjConfigType.valueOf(type)) {
            case condition:
                objVO.setConditionConfig(configs);
                break;
            case title:
                objVO.setTitleConfig(configs);
                break;
            case form:
                objVO.setFormConfig(configs);
                break;
            case preview:
                objVO.setPreviewConfig(configs);
                break;
            case appletTitle:
                objVO.setAppletTitleConfig(configs);
                break;
            default:
        }
        update(objVO);

    }

    @Override
    public List<EntitySchema.AttrConfig> getConfig(String objId, String type) throws BusinessException {
        EntitySchema vo = queryById(objId);
        List<EntitySchema.AttrConfig> list = vo.getTitleConfig();
        switch (ObjConfigType.valueOf(type)) {
            case condition:
                list = vo.getConditionConfig();
                break;
            case title:
                list = vo.getTitleConfig();
                break;
            case form:
                list = vo.getFormConfig();
                break;
            case preview:
                list = vo.getPreviewConfig();
                break;
            case appletTitle:
                list = vo.getAppletTitleConfig();
                break;
            default:
        }
        return list;
    }

    @Override
    public void addedObj(EntitySchema objVO) throws BusinessException {
        Obj obj = baseMongoDao.get(objVO.getId(), Obj.class);
        if (ObjStatus.published.name().equals(obj.getStatus())) return;
        obj.setStatus(ObjStatus.published.name());
        baseMongoDao.update(obj);
        /**
         * 更新索引
         */
        Executors.newCachedThreadPool().execute(() -> {
            List<Obj.Attr> attrs = obj.getAttrs();
            if (!CommonUtil.listIsBlank(attrs)) {
                List<String> indexes = attrs.stream().filter(Obj.Attr::getIsIndex).collect(Collectors.toList())
                        .stream().map(Obj.Attr::getCode).collect(Collectors.toList());
                indexes.add("isDelete");
                indexes.add("tenantId");
                log.error(Arrays.toString(indexes.toArray()));
                baseMongoDao.insertIndex("entity_" + obj.getId(), indexes);
            }
        });

        // 更新网页
        WebPageVO vo = new WebPageVO();
        vo.setUri(obj.getId());
        List list = webPageService.list(vo);
        if (list != null && !list.isEmpty()) return;
        vo.setName(obj.getName());
        vo.setTenantId(objVO.getTenantId());
        vo.setCreateId(objVO.getCreateId());
        DictVO dict = new DictVO();
        dict.setId("624e5135a6b9f60c62bc2b9b");
        dict.setValue("obj");
        vo.setType(dict);
        List<Obj.Button> buttons = obj.getButtonConfig();
        List<Obj.Button> deftButton = ObjButton.deftButtons;
        if (buttons != null) {
            deftButton.addAll(buttons);
        }
        vo.setButtons(deftButton.stream().map(m -> DataUtil.o2o(m, ButtonVO.class)).collect(Collectors.toList()));
        webPageService.add(vo);
    }

    @Override
    public void addedObj() throws BusinessException {
        List<Obj> list = baseMongoDao.list(Obj.class, Obj.class);
        list.forEach(objVO -> {
            WebPageVO vo = new WebPageVO();
            vo.setName(objVO.getName());
            vo.setUri(objVO.getId());
            vo.setTenantId(objVO.getTenantId());
            vo.setCreateId(objVO.getCreateId());
            DictVO dict = new DictVO();
            dict.setId("624e5135a6b9f60c62bc2b9b");
            dict.setValue("obj");
            vo.setType(dict);
            List<Obj.Button> buttons = objVO.getButtonConfig();
            List<Obj.Button> deftButton = ObjButton.deftButtons;
            if (buttons != null) {
                deftButton.addAll(buttons);
            }
            vo.setButtons(deftButton.stream().map(m -> DataUtil.o2o(m, ButtonVO.class)).collect(Collectors.toList()));
            try {
                webPageService.add(vo);
            } catch (BusinessException e) {
                e.printStackTrace();
            }
        });
    }

    @Override
    public Page<EntitySchema> listPage(PageRow<ConditionsVO> pageRow) {
        List<AggregationOperation> operations = new ArrayList<>();
        List<Criteria> criteriaList = baseMongoDao.condition2CriteriaList(pageRow.getCondition());
        Criteria criteria = new Criteria().andOperator(criteriaList);
        operations.add(Aggregation.match(criteria));
        BaseVO.Sort bSort = pageRow.getCondition().getSortCondition();
        try {
            Sort sort = Sort.by(Sort.Direction.fromString(bSort.getDirection()), bSort.getCode());
            SortOperation sortOperation = Aggregation.sort(sort);
            operations.add(sortOperation);
        } catch (Exception e) {
        }
        SkipOperation skipOperation = Aggregation.skip(Long.valueOf(pageRow.getPageNo() * pageRow.getPageSize()));
        operations.add(skipOperation);

        LimitOperation limitOperation = Aggregation.limit(pageRow.getPageSize());
        operations.add(limitOperation);

        operations.add(Aggregation.lookup("dict", "group._id", "_id", "group"));
        operations.add(Aggregation.unwind("group", true));
//        operations.add(Aggregation.addFields().addFieldWithValue("group.id", ConvertOperators.Convert.convertValue("$group._id").to(JsonSchemaObject.Type.STRING)).build());
        List<EntitySchema> l = baseMongoDao.aggregate(operations, "obj", EntitySchema.class).getMappedResults();
        long count = baseMongoDao.count(criteria, Obj.class);
        return new Page<>(l, pageRow.getPageSize(), pageRow.getPageNo() + 1, count);
    }

    @Override
    public List<EntitySchema> list(ConditionsVO vo) {
        List<AggregationOperation> operations = new ArrayList<>();
        String tenantId = vo.getTenantId();
        vo.setTenantId(null);
        List<Criteria> criteriaList = baseMongoDao.condition2CriteriaList(vo);
        criteriaList.add(Criteria.where("tenantId").in(getTenantId(tenantId)));
        Criteria criteria = new Criteria().andOperator(criteriaList);
        operations.add(Aggregation.match(criteria));
        BaseVO.Sort bSort = vo.getSortCondition();
        try {
            Sort sort = Sort.by(Sort.Direction.fromString(bSort.getDirection()), bSort.getCode());
            SortOperation sortOperation = Aggregation.sort(sort);
            operations.add(sortOperation);
        } catch (Exception e) {
        }
        operations.add(Aggregation.lookup("dict", "group._id", "_id", "group"));
        operations.add(Aggregation.unwind("group", true));
        List<EntitySchema> l = baseMongoDao.aggregate(operations, "obj", EntitySchema.class).getMappedResults();
        return l;
    }

    public List<String> getTenantId(String tenantId){
        String currId = tenantId;
        boolean flag = true;
        Set<String> ids = new HashSet<>();
        ids.add(currId);
        while (flag){
            TenantVO tenantP =  mongoTemplate.findById(new ObjectId(currId), TenantVO.class,"tenant");
            if(tenantP.getTenantId().equals(currId))flag= false;
            currId = tenantP.getTenantId();
            ids.add(currId);
        }
        return new ArrayList<>(ids);
    }

    @Override
    public EntitySchema queryById(String id, UserVO user) throws BusinessException {
        EntitySchema vo = queryById(id);
        if (user == null) return vo;
        RoleVO roleVO = DataUtil.mergeRole(user.getRole(), user.getSelfRole());
        if (roleVO == null) return vo;
        Map<String, RoleVO.DataConfig> map = roleVO.getDataConfigs();
        if (null == map) return vo;
        RoleVO.DataConfig config = map.get(id);
        if (null == config) return vo;
        List<String> notWriteAttrs = config.getNotWriteAttr();
        if (notWriteAttrs != null) {
            List<EntitySchema.AttrConfig> list = vo.getFormConfig();
            if (list != null) {
                int size = list.size();
                for (int i = size - 1; i >= 0; i--) {
                    EntitySchema.AttrConfig conf = list.get(i);
                    if (notWriteAttrs.contains(conf.getCode())) list.remove(i);
                }
            }
        }
        List<String> notReadAttrs = config.getNotReadAttr();
        if (notReadAttrs != null) {
            List<EntitySchema.AttrConfig> list = vo.getTitleConfig();
            if (list != null) {
                int size = list.size();
                for (int i = size - 1; i >= 0; i--) {
                    EntitySchema.AttrConfig conf = list.get(i);
                    if (notReadAttrs.contains(conf.getCode())) list.remove(i);
                }
            }
        }
        return vo;
    }

    @Override
    public void cloneObj(EntitySchema objVO) throws BusinessException {
        Obj obj = baseMongoDao.get(objVO.getId(), Obj.class);
        obj.setId(null);
        obj.setStatus(ObjStatus.deft.name());
        obj.setName(obj.getName() + "_copy");
        obj.setCreateId(objVO.getCreateId());
        obj.setCreateTime(objVO.getCreateTime());
        obj.setDepartmentId(objVO.getDepartmentId());
        baseMongoDao.save(obj);
    }

    @Override
    public void add(EntitySchema objVO) throws BusinessException {
        Obj obj = DataUtil.o2o(objVO, Obj.class);
        obj.setId(null);
        obj.setStatus(ObjStatus.deft.name());
        baseMongoDao.save(obj);
    }

    @Override
    public void batchAdd(List<EntitySchema> list) throws BusinessException {

    }

    @Override
    public EntitySchema queryById(String id) throws BusinessException {
        Obj obj = baseMongoDao.get(id, Obj.class);
        if (obj == null) {
            throw new BusinessException(ErrCode.OBJ_NOT_EXIST);
        }
        if (ObjectUtils.isEmpty(obj.getSortConditions())&&(obj.getSortCondition() == null || !StringUtils.hasText(obj.getSortCondition().getCode())))
            obj.setSortCondition(BasePO.Sort.defaultSort());
        EntitySchema vo = DataUtil.o2o(obj, EntitySchema.class);
//        List<ObjVO.AttrVO> attrs = vo.getAttrs();

//        if (!CommonUtil.listIsBlank(attrs)) {
//            List<ObjVO.AttrVO> l = new ArrayList<>();
//            l.addAll(attrs);
//            Collections.sort(l);
//            Deque<ObjVO.AttrVO> nodes = new LinkedList<>();
//            for (ObjVO.AttrVO attr : attrs) {
//                if (StringUtils.isEmpty(attr.getPreCode())) {
//                    l.add(attr);
//                } else {
//                    nodes.offer(attr);
//                }
//
//            }
//            Map<String, Integer> temp = new HashMap<>();
//            int nodeSize = nodes.size();
//            while (!nodes.isEmpty()) {
//                ObjVO.AttrVO node = nodes.poll();
//                if (temp.getOrDefault(node.getCode(), 0) >= nodeSize) {
//                    l.add(node);
//                    continue;
//                }
//                AtomicInteger index = new AtomicInteger();
//                ObjVO.AttrVO preNode = l.stream().filter(f -> {
//                    index.getAndIncrement();
//                    return f.getCode().equals(node.getPreCode());
//                }).findFirst().orElse(null);
//                if (preNode == null) {
//                    nodes.offer(node);
//                    temp.put(node.getCode(), temp.getOrDefault(node.getCode(), 0) + 1);
//                } else {
//                    l.add(index.get(), node);
//                }
//            }
//            vo.setAttrs(l);
//        }
        return vo;
    }

    @Override
    public List<EntitySchema> queryByIds(List<String> ids) {
        return null;
    }

    @Override
    public EntitySchema queryByCondition(EntitySchema objVO) throws BusinessException {
        objVO.setStatus(ObjStatus.published.name());
        return baseMongoDao.queryOne(Obj.class, EntitySchema.class, objVO);
    }

    @Override
    public void update(EntitySchema objVO) throws BusinessException {
        baseMongoDao.update(DataUtil.o2o(objVO, Obj.class));
    }

    @Override
    public Page<EntitySchema> list(PageRow<EntitySchema> pageRow) {
        return null;
    }

    @Override
    public List<EntitySchema> list(EntitySchema condition) {
        List<AggregationOperation> operations = new ArrayList<>();
        Criteria criteria = baseMongoDao.condition2Criteria(condition);
        operations.add(Aggregation.match(criteria));
        operations.add(Aggregation.lookup("dict", "group._id", "_id", "group"));
        operations.add(Aggregation.unwind("group", true));
//        operations.add(Aggregation.addFields().addFieldWithValue("group.id", ConvertOperators.Convert.convertValue("$group._id").to(JsonSchemaObject.Type.STRING)).build());
        List<EntitySchema> l = baseMongoDao.aggregate(operations, "obj", EntitySchema.class).getMappedResults();
        return l;
    }

    @Override
    public void deleteById(String id) throws BusinessException {
        WebPageVO cond = new WebPageVO();
        cond.setUri(id);
        if (baseMongoDao.exists(cond, "webPage")) {
            throw new BusinessException(ErrCode.OBJ_USEING);
        }

        Obj app = new Obj();
        app.setId(id);
        app.setIsDelete(YesOrNo.YES.ordinal());
        baseMongoDao.update(app);
    }

    @Override
    public void delete(List<String> ids) throws BusinessException {

    }
}
